﻿#region Copyrights Notice
//©2010 Microsoft Corporation. All rights reserved.
#endregion

#region usings

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Web.Administration;
using System.DirectoryServices;
using Microsoft.InformationSecurity.CISF.Common.Utilities;
using System.Xml;
#endregion

namespace CISFAppDeployer
{
    public class IISAdmin : IDisposable 
    {

        #region User Account Management

        private string serverName;
        private ServerManager serverMgr;
        private string webSite;

        private bool disposed;

        public IISAdmin(string iisServerName, string webSiteName)
        {
            serverName = iisServerName;
            webSite = webSiteName;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    serverMgr.Dispose();
                    disposed = true;
                }
            }
        }

        ~IISAdmin()
        {
            Dispose(false) ;  
        }

        #region Validation funtions

        public bool IsValidSiteName()
        {
            try
            {
                if (string.IsNullOrEmpty(webSite))
                {
                    Logger.WriteLine("Invalid website in config file", "Error");
                    throw new ArgumentNullException("webSite", "Invalid webSite");
                }
                //get server instance
                ServerManager mgr = GetServerInstance();
                Site site = mgr.Sites[webSite];

                if (null == site)
                {
                    Logger.WriteLine("Invalid website in config file", "Error");
                    throw new ArgumentException("webSite", "Invalid website");
                }
                return true;
            }
            catch
            {
                Logger.WriteLine("Invalid server or website in config file", "Error");
                throw new ArgumentException("webSite", "Invalid website");
            }
        }

        public bool IsApplicationExisting(string applicationName)
        {
            ServerManager mgr = GetServerInstance();
            Site site = mgr.Sites[webSite];

            Application app = site.Applications[@"/Applications/" + applicationName];
            if(null == app)
                return false;
            else 
                return true;
        }
        #endregion

        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="userName"></param>
        ///// <param name="password"></param>
        ///// <returns></returns>
        //public bool CreateLocalUserAccount(string userName, string password)
        //{
        //    try
        //    {
        //        if (string.IsNullOrEmpty(userName))
        //            throw new ArgumentNullException("userName", "Invalid User Name.");
        //        if (string.IsNullOrEmpty(password))
        //            throw new ArgumentNullException("password", "Invalid Password.");

        //        DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer");
        //        bool userFound = false;
        //        try
        //        {
        //            if (directoryEntry.Children.Find(userName, "user") != null)
        //                userFound = true;
        //        }
        //        catch
        //        {
        //            userFound = false;
        //        }
        //        if (!userFound)
        //        {
        //            DirectoryEntry newUser = directoryEntry.Children.Add(userName, "user");
        //            newUser.Invoke("SetPassword", new object[] { password });
        //            newUser.Invoke("Put", new object[] { "Description", "Application Pool User Account" });
        //            newUser.CommitChanges();
        //            newUser.Close();
        //        }
        //    }
        //    catch
        //    {
        //        throw;
        //    }
        //    return true;
        //}

        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="userName"></param>
        ///// <returns></returns>
        //public bool RemoveLocalUserAccount(string userName)
        //{
        //    try
        //    {
        //        if (string.IsNullOrEmpty(userName))
        //            throw new ArgumentNullException("userName", "Invalid User Name.");

        //        DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer");
        //        bool userFound = false;
        //        try
        //        {
        //            if (directoryEntry.Children.Find(userName, "user") != null)
        //                userFound = true;
        //        }
        //        catch
        //        {
        //            userFound = false;
        //        }
        //        if (userFound)
        //        {
        //            directoryEntry.Children.Remove(directoryEntry.Children.Find(userName, "user"));
        //        }
        //    }
        //    catch
        //    {
        //        throw;
        //    }
        //    return true;
        //}
        #endregion

        #region IIS Administration
        
        public ServerManager GetServerInstance()
        {
            if (string.IsNullOrEmpty(serverName))
                throw new ArgumentNullException("remoteServerName", "GetServerInstance: Server Name is null or empty.");

            if (serverMgr == null)
            {
                serverMgr = ServerManager.OpenRemote(serverName);
                if (null == serverMgr)
                {
                    Logger.WriteLine("Could not open connection to " + serverMgr, "Error");
                    throw new ArgumentNullException("serverName", "GetServerInstance: Invalid server name.");
                }
                return serverMgr; 
            }
            else
                return serverMgr;
        }

        public string GetPortalRoot()
        {
            ServerManager mgr = GetServerInstance();
            Site site = mgr.Sites[webSite];

            return site.Applications["/"].VirtualDirectories["/"].PhysicalPath.ToString();
        }
        /// <summary>
        /// Creates AppPool in IIS
        /// </summary>
        /// <param name="applicationPoolName"></param>
        /// <returns></returns>
        public bool CreateApplicationPool(string applicationPoolName, bool IsCustomAppPoolAccount, 
                                                string strAppPoolActName, string strAppPoolActPassword)
        {
            try
            {
                Logger.WriteLine("    Creating Application Pool...", "");

                //get server instance
                ServerManager mgr = GetServerInstance();

                //check if AppPol already exists
                ApplicationPool extAppPool = mgr.ApplicationPools[applicationPoolName];
                if (extAppPool == null)
                {
                    ApplicationPool newAppPool = mgr.ApplicationPools.Add(applicationPoolName);
                    if (IsCustomAppPoolAccount)
                    {
                        newAppPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
                        newAppPool.ProcessModel.UserName = strAppPoolActName;
                        newAppPool.ProcessModel.Password = strAppPoolActPassword;
                    }
                    else
                    {
                        newAppPool.ProcessModel.IdentityType = ProcessModelIdentityType.NetworkService;
                    }

                    newAppPool.ManagedRuntimeVersion = "v2.0";
                    newAppPool.AutoStart = true;
                    newAppPool.Enable32BitAppOnWin64 = true;
                    newAppPool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
                    newAppPool.QueueLength = 1000;//default
                    newAppPool.ProcessModel.IdleTimeout = new TimeSpan(0, 20, 0);//default
                    newAppPool.Recycling.PeriodicRestart.PrivateMemory = 0;//default
                    newAppPool.Recycling.PeriodicRestart.Time = new TimeSpan(0, 20, 0);//default

                    //mgr.CommitChanges();
                }
                else
                {
                    Logger.WriteLine("    Application Pool already exists", "Warning");
                }

            }
            catch 
            {
                throw;
            }
            return true;
        }

        /// <summary>
        /// Adds Application to the Website
        /// </summary>
        /// <param name="applicationPath"></param>
        /// <param name="applicationPool"></param>
        /// <param name="physicalPath"></param>
        /// <returns></returns>
        public bool AddApplication(string applicationPath, string applicationPool, string physicalPath)
        {
            Logger.WriteLine("    Creating virtual application in Portal...", "");

            try
            {
                //get server instance
                ServerManager mgr = GetServerInstance();

                Site site = mgr.Sites[webSite];
                if (site == null)
                    return false;

                Application app = site.Applications[applicationPath];

                if (app != null)
                {
                    Logger.WriteLine("    Application already exists", "Warning");
                    return true;
                }
                else
                {
                    app = site.Applications.CreateElement();
                    app.Path = applicationPath;
                    app.ApplicationPoolName = applicationPool;

                    //creating app
                    app.VirtualDirectories.Add("/", physicalPath);
                }
                site.Applications.Add(app);

                mgr.CommitChanges();
                return true;
            }
            catch
            {
                throw;
            }
        }

        public bool RemoveApplication(string applicationPath)
        {
            Logger.WriteLine("    Deleting virtual application in Portal...", "");

            ServerManager mgr = GetServerInstance();
            Site site = mgr.Sites[webSite];

            Application app = site.Applications[applicationPath];
            if (app == null)
            {
                Logger.WriteLine("    Application does not exist", "Warning");
                return true;
            }
            else
            {
                //remove the applciation pool and then delete the applciation
                RemoveApplicationPool(app.ApplicationPoolName);

                site.Applications.Remove(app);
                mgr.CommitChanges();

            }
            return true;
        }

        private bool RemoveApplicationPool(string applicationPoolName)
        {
            Logger.WriteLine("    Deleting Application Pool...", "");

            ServerManager mgr = GetServerInstance();
            //check if AppPol already exists
            ApplicationPool extAppPool = mgr.ApplicationPools[applicationPoolName];
            //check if any other application is using the same App pool
            if (extAppPool != null)
            {
                if (false == IsAppPoolInUse(applicationPoolName))
                {
                    mgr.ApplicationPools.Remove(extAppPool);
                    //mgr.CommitChanges();
                }
            }
            return true;
        }

        #endregion

        #region Sitemap Updates

        /// <summary>
        /// Updates Application sitemaps
        /// </summary>
        /// <param name="strAppDestFolder">Destination Folder in Portal</param>
        /// <param name="strWebsite">Portal Website name</param>
        /// <param name="currApp">Application Name</param>
        /// <returns></returns>
        public bool UpdateApplicationSitemaps(string currApp, string strAppDestFolder, bool installFlag)
        {
            Logger.WriteLine("    Updating applications sitemap settings...", "");
            try
            {
                //read the newly created application settings
                //get the curret app details to propagate
                //process current file
                string sitemappath = strAppDestFolder + currApp;
                SiteMapManager sm = new SiteMapManager();
                XmlElement smElement = sm.GetSiteMapNode(sitemappath);
                string currAppTitle = smElement.GetAttribute("title").ToString();
                string currAppURL = smElement.GetAttribute("url").ToString();
                string currAppDesc = smElement.GetAttribute("description").ToString();

                ServerManager mgr = GetServerInstance() ;
                Site site = mgr.Sites[webSite];
 
                ApplicationCollection appCol = site.Applications;
                if (appCol != null)
                {
                    foreach (Application app in appCol)
                    {
                        if (app.Path.StartsWith("/Applications/"))
                        {
                            //Except current app update sitemap links with current app details
                            if (currApp != app.Path.Substring(14))
                            {
                                //setting document to update
                                sm.docName = strAppDestFolder + app.Path.Substring(14) + "\\web.sitemap";

                                //updating web.sitemap with the new application node
                                sm.Deployer_UpdateApplicationSitemapNodes(currAppTitle, currAppURL, currAppDesc, installFlag);
                            }
                            else
                            {
                                if (installFlag)
                                {
                                    //setting document to update
                                    sm.docName = strAppDestFolder + app.Path.Substring(14) + "\\web.sitemap";
                                    //updating archived applications (not part of virtual apps)
                                    sm.Deployer_UpdateAppSitemapNodesWithArchivedApps();

                                    //updating existing apps
                                    UpdateCurrentApplicationSitemap(strAppDestFolder, app.Path.Substring(14).ToString());

                                    //set the current application sitemap
                                    sm.SetSiteMapNode("AppWeb.sitemap");
                                }
                            }
                        }
                    }
                }
            }
            catch
            {
                Logger.WriteLine("Error updating (" + currApp + ") application sitemap...", "Error");
                return false;
            }

            //finally...
            return true;
        }

        /// <summary>
        /// Updates Current Application sitemaps
        /// </summary>
        /// <param name="strAppDestFolder">Destination Folder in Portal</param>
        /// <param name="strWebsite">Portal Website name</param>
        /// <param name="currApp">Application Name</param>
        /// <returns></returns>
        public void UpdateCurrentApplicationSitemap(string strAppDestFolder, string currApp)
        {
            try
            {
                //read the newly created application settings
                string sitemappath = String.Empty;
                string currAppURL = String.Empty;
                string currAppTitle = String.Empty;
                string currAppDesc = String.Empty;
                SiteMapManager sm = new SiteMapManager();

                ServerManager mgr = GetServerInstance();
                Site site = mgr.Sites[webSite];
                ApplicationCollection appCol = site.Applications;
                if (appCol != null)
                {
                    foreach (Application app in appCol)
                    {
                        if (app.Path.StartsWith("/Applications/"))
                        {
                            //Except current app update sitemap links with current app details
                            if (currApp != app.Path.Substring(14))
                            {
                                //get the curret app details to propagate
                                //process current file
                                try
                                {
                                    sitemappath = strAppDestFolder + app.Path.Substring(14);
                                    XmlElement smElement = sm.GetSiteMapNode(sitemappath);
                                    //getting sitemap attributes
                                    currAppTitle = smElement.GetAttribute("title").ToString();
                                    currAppURL = smElement.GetAttribute("url").ToString();
                                    currAppDesc = smElement.GetAttribute("description").ToString();

                                    //setting document to update
                                    sm.docName = strAppDestFolder + currApp + "\\web.sitemap";

                                    //updating web.sitemap with the new application node
                                    sm.Deployer_UpdateApplicationSitemapNodes(currAppTitle, currAppURL, currAppDesc, true);
                                }
                                catch (System.IO.FileNotFoundException )
                                {
                                    //if webSite get adn file not found exception suppress, the exception, log it and procedd with other applciaitons
                                }
                            }
                        }
                    }
                }
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// Checks whether the application pool is shared with any other application. If it is not, the function returns
        /// false and the application pool can be deleted
        /// </summary>
        /// <param name="applicationPoolName">Name of the applicaiton pool</param>
        /// <returns>true or false</returns>
        private bool IsAppPoolInUse(string applicationPoolName)
        {
            ServerManager srvMgr = GetServerInstance();
            Site site = srvMgr.Sites[webSite];
            ApplicationCollection appCollection = site.Applications;
            int appCount = 0;
            foreach (Application app in appCollection)
            {
                if (string.Compare(app.ApplicationPoolName, applicationPoolName, true) == 0)
                {
                    appCount++;
                }
            }
            if (appCount > 1)
                return true;
            else
                return false;
        }

        #endregion
    }
}
